Node.js TCP Server

TCP Server 通过 Node 中的 net 模块来实现 TCP 四层模型(应用层、传输层、网络层、链路层)

Id Name Info Protocol
1 链路层(Link layer) 负责在以太网、Wifi 底层网络发送数据包,并通过网卡、使用 MAC 地址来标记网络上的设备 GPRS、IEEE、IPoe、Localhost、TRILL、IPoAC、FDDI、NAK\NACK、SEND、frame\frame relay、ATM、Data Link Layer、PPP、STP、L2TP、ARQ、CDPD、LLDP、LCP、Link Aggregation、HDLC
2 网络层\网络互联层(Internet layer) 定义了 IP 地址的概念,通过 IP 地址取代 MAC 地址,并将局域网、广域网链接成一个虚拟网络,主要目的就是 将 IP 地址翻译成 MAC 地址就可查找设备 IPsec、IPv4\IPv5\IPv6\IPv9、 IP Address、x.25、IPX、ICMP\ICMPv6、IGMP、DDP、Mobile IP、Network Layer、PPP、RSVP、Anti-replay
3 传输层(Transport layer) 保证数据在 IP 地址标记的两点之间进行传输 TCP、UDP、TLS\SSL、DCCP、SCTP、RSVP
4 应用层(Application layer) 直接与应用程序和接口结合,并提供常见的应用服务 DHCP、DNS、FTP、gopher、HTTP\HTTP-2、IMAP4、IRC、NNTP、XMPP、POP3、SIP、SMTP、SNMP、SSH、TELNET、RPC、RTCP、RTP、RTSP、SDP、SOAP、GTP、STUN、NTP、SSDP

对于每个网络层次,Node 都提供了相应的模块,如 http、net、tls/crypto、dgram 等,其中 net、http、dgram 模块分别实现和提供 TCP、HTTP 的通信,其中 http 为应用层模块,而 net 为传输层模块。

.http 是一个综合模块,包含了 http/tls/crypto 等,这将用于确保传输的安全性。其中 net 模块是 node 中的核心模块,严谨的来说 http.Server 继承了 net.Server,除此之外 HTTP SERVER/CLIENT 都以来与 net.Socket,而 net 模块主要分为:

  1. net.Server:通过其内部的 socket 来提供客户端的通信
  2. net.Socket:TCP/Localhost socket 的实现,并提供了全双工的 stream 接口

Network socket (网络套接字)是指网络中不同主机上应用程序之间的通信端点对象,一个套接字就是一个的另一端,也就是以 IP 及端口所组成的地址被称之为套接字地址(socket address)

Create TCP Server

1
2
3
4
5
6
7
8
9
var net = require('net')

var server = net.createServer(function (socket) {
console.log('someone sonnects')
})

server.listen(8210, function () {
console.log('Create server on http://127.0.0.1:8210')
})

创建一个 TCP 服务可以通过 net.createServer 方法进行创建,可通过 server.listen 来设置服务的监听端口以及,当然一旦运行服务时除发的还是 server 下的 listening 事件,因此也支持另一种写法:

net.Server()

1
2
3
4
5
6
7
8
9
10
11
var net = require('net')

var server = net.createServer(function (socket) {
console.log('someone sonnects')
})

server.listen(8210)

server.on("listening", function () {
console.log("Create server on http://127.0.0.1:8210/")
})

除此之外 server 还支持另外几种 TCP 事件:

Id Name Info
1 listening 调用 server.listen(),即当开始监听时候除发
2 connection 当有新创建时后除发,回调的参数是 socket 连接的对象
3 close 当关闭时触发,回调函数没有参数
4 error 当 TCP 服务发生错误时触发,回调参数为 error
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var net = require('net')

var server = new net.Server()

// 监听新的请求时返回回调函数
server.on("connection", function (socket) {
console.log("connects someone")
})

server.listen(8210)

// 监听时触发
server.on("listening", function () {
console.log("Create server on http://127.0.0.1:8210/")
})

// 关闭触发回调函数
server.on("close", function () {
console.log("close server")
})

// 错误时返回
server.on("error", function (err) {
console.log("server error!")
})

server.address()

如果需要监听 IP 套接字,也就是套接字地址(socket address),可以通过 server.address() 方法来实现,他主要提供了三个服务器绑定的 address 以及 familyport

1
2
3
4
5
6
7
8
9
10
11
12
var net = require('net')

var server = net.createServer(function (socket) {
console.log("connects someone")
})

server.listen(8210, function () {
var address = server.address()
console.log("TCP Server 所监听的端口号:" + address.port)
console.log("TCP Server 所监听的地址:" + address.address)
console.log("TCP Server IPv4 or IPv6:" + address.family)
})

需要注意的是如果监听的端口号是 IPv6 地址,那么 address 则返回 ::

server.getConnections()

.server.getConnections 方法用于异步获取服务器上的并发连接数,回调参数分为 errcount

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const net = require("net")

/*
Create server on http://127.0.0.1:8210
someone connects
This is client count: 1
*/
var server = net.createServer(function (socket) {
console.log('someone connects')

server.maxConnections=3
server.getConnections(function (err,count) {
console.log("This is client count: " + count)
})
})

server.listen(8210, function () {
console.log("Create server on http://127.0.0.1:8210")
})

之后我们分别通过 GET 请求 http://127.0.0.1:8210/ 并通过代理访问 uri 则有三个 IP 访问,因此分别依次循环输出 This is Client count: 1~3

Server at Client 之间的通信

Server

服务端主要用于接收客户端所发送的消息,因此只需要通过 data 事件来接收数据时进行输出,在收到数据之前首先通过 address() 来将转换为 JSON 格式并输出,并通过 socket.bytesWritten 来计算客户端所发送的字节数,最后通过 socket.bytesRead 方法来统计书数据的大小。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
const net = require('net')

/*
Create server on http://127.0.0.1/8210
The is address is {"address":"::","family":"IPv6","port":8210} 以发送
消息大小为 62
Massage from client
数据的大小为:19
*/
var server = net.createServer(function (socket) {
var addresss = server.address()
var message = "The is address is " + JSON.stringify(addresss)

socket.write(message,function () {
var writeSize = socket.bytesWritten
console.log(message + " 以发送")
console.log("消息大小为 " + writeSize)
})

// 收到数据时回调
socket.on('data', function (data) {
console.log(data.toString())
var readSize = socket.bytesRead
console.log("数据的大小为:" + readSize)
})
})

server.listen(8210,function () {
console.log("Create server on http://127.0.0.1/8210")
})

Client

在 Server 与 Client 建立链接的过程中,客户端扮演的是数据发送者的身份,而他主要的作用就是与服务端建立链接并发送数据,之后监听服务端的状态,在这期间主通过 client.connect 来链接服务,之后使用其 client.write 来写入数据并发送。

net.Socket 类

API

对于 net.Socket 相关的 API Node 提供了多种方法:

Id Name Info
1 socket.address() 返回操作系统报告并绑定 address,和套接字的 port 以及 family 等
2 socket.bytesRead() 接收的字节数
3 socket.bytesWritten() 发送的字节数
4 socket.connect(options[,sonnectListener]) 指定套接字链接
options
port 套接字应该链接到的端口(必填)
host 套接字应连接到的主机(默认为 localhost
localAddress 套接字应该链接到本地地址
localPort 套接字应链接本地端口
family IP 堆栈的版本(通常是 IPv6、IPv4或0,而 0 表示 允许 IPv4/6 地址)
hints 可选的 dns.lookup() 提示(主机名)
lookup 自定义查找函数(默认 dns.lookup()
5 socket.connect(port[,host][,connectListener]) 指定套接字链接
port 客户端应该链接到的端口
host 客户端应该链接到的主机
connectListener 方法的常用参数,将被添加为 connect 事件监听
6 socket.destroyed 半关闭套接字,服务端关闭时发送数据包给客户端,客户端关闭
7 socket.localAddress 远程客户端链接本地 IP 地址(以 IP 地址的字符串形式表示,如 0.0.0.0
8 socket.localPort(socket.remotePort) 本地端口数字表示(如 8210
9 socket.remoteAddress 远程 IP 地址的字符串形式输出(如 74.125.127.1002001:4860:a005::68
10 socket.remoteFamily 远程 IP 系列的字符串形式表示(如 IPv6IPv4
11 socket.setTimeout(socket.timeout) 用于进行设置连接(如果 timeout 为 0,则禁用空闲超时)
12 socket.setKeepAlive() 用于设置和i长连接
13 socket.destroy()\socket.destroyed 当错误发生时,用于销毁 socket,以确保这个 socket 上不会有其他 IO 操作
事件
Id Name Info
1 close 当链接断开时触发,假设因为传输错误导致断开,则参数为 err
2 connect 当建立成功套接字链接时触发
3 data 接收到数据时触发
4 drain 当写缓存空了时触发
5 end 当另一端结束时触发
6 error 当错误时触发
7 lookup 当解析完主机名后触发
8 ready 当套接字准备好使用时触发
9 timeout 提示用户 socket 超时,需要手动关闭链接
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const net = require('net')

/*
Connnect to Server
The is address is {"address":"::","family":"IPv6","port":8210}
*/
var client = new net.Socket()

// 链接服务器,"connect" 是指当成功建立连接时触发
client.connect(8210,'127.0.0.1', function () {
console.log("Connnect to Server")

// 与 Server 建立链接
client.write("Massage from client")
})

client.on("data", function (data) {
console.log(data.toString())
})

// 当套接字另一端断开链接时出发回调,从而结束套接字可读端
client.on("end", function () {
console.log("end Server")
})

本文使用《江雪分析公开知识存储库知识共享许可证》进行发布